package com.ahdms.es.engine;

import com.ahdms.es.bean.CertInfo;
import com.ahdms.es.bean.enums.VerifyCertCode;
import com.ahdms.es.exceptions.ApiException;
import com.ahdms.es.exceptions.VerifyCertException;
import com.ahdms.es.gm.constant.IKIObjectIdentifiers;
import com.ahdms.es.result.VerifyResult;
import com.ahdms.es.util.DataConvert;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.TBSCertificate;

import java.util.Date;
import java.util.Optional;

/**
 * @author qinxiang
 * @date 2021-04-09 9:31
 */
public abstract class X509CertVerifyEngine {

    public static VerifyResult<CertInfo> verifyX509Cert(Certificate cert) {
        try {
            return getInstance(cert).verifyCert(cert);
        }catch (ApiException e){
            return VerifyResult.error(VerifyCertCode.CERT_Algorithm_Invalid);
        }
    }

    public static X509CertVerifyEngine getInstance(Certificate cert){
        ASN1Encodable pkAlgorithmIdentifier = cert.getSubjectPublicKeyInfo().getAlgorithm().getParameters();
        if (IKIObjectIdentifiers.sm_puk_combined.equals(pkAlgorithmIdentifier)) {
            return new IKICertVerifyEngine();
        } else if (IKIObjectIdentifiers.sm_2.equals(pkAlgorithmIdentifier)) {
            return new PKICertVerifyEngine();
        }
        throw new ApiException(VerifyCertCode.CERT_Algorithm_Invalid.getMessage());
    }

    /**
     * 验证证书有效性
     *
     * @param cert
     * @return
     */
    public VerifyResult<CertInfo> verifyCert(Certificate cert) {
        try {
            if(!verifyCertTrustChain(cert)){
                return VerifyResult.error(VerifyCertCode.Trust_Chain_Fail);
            }
            verifyCertDate(cert);
            if(!verifyCertKeyUsage(cert)){
                return VerifyResult.error(VerifyCertCode.Verify_KeyUsage_Fail);
            }
            if(!verifyCertIsRevoke(cert)){
                return VerifyResult.error(VerifyCertCode.Verify_Revoke_Fail);
            }
            return VerifyResult.success(DataConvert.certToCertInfo(cert));
        } catch (VerifyCertException e) {
            e.printStackTrace();
            return VerifyResult.error(e.getAlertAble());
        }
    }

    /**
     * 验证证书信任链
     *
     * @param cert
     * @return
     */
    public abstract boolean verifyCertTrustChain(Certificate cert) throws VerifyCertException;

    /**
     * 验证证书有效期
     *
     * @param cert
     * @return
     */
    public boolean verifyCertDate(Certificate cert) throws VerifyCertException {
        Date nowDate = new Date();
        if (nowDate.before(cert.getStartDate().getDate())) {
            throw new VerifyCertException(VerifyCertCode.Vaild_Date_ineffective);
        }
        if (nowDate.after(cert.getEndDate().getDate())) {
            throw new VerifyCertException(VerifyCertCode.Vaild_Date_EXPIRED);
        }
        return true;
    }

    /**
     * 通过CRL验证证书是否被吊销
     *
     * @param cert
     * @return
     */
    public abstract boolean verifyCertIsRevoke(Certificate cert);

    /**
     * 通过ocsp验证证书是否被吊销
     * @param cert
     * @return
     */
    public abstract boolean verifyCertByOcsp(Certificate cert);

    /**
     * 验证证书密钥用法
     *
     * @param cert
     * @return
     */
    public boolean verifyCertKeyUsage(Certificate cert) {
        return Optional.ofNullable(cert).map(Certificate::getTBSCertificate)
                .map(TBSCertificate::getExtensions)
                .map(KeyUsage::fromExtensions)
                .map(keyUsage -> keyUsage.hasUsages(KeyUsage.digitalSignature))
                .orElse(false);
    }

}
